package leetcode.tree.binary.traversal;

import leetcode.tree.binary.TreeNode;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

/**
 * 94. 二叉树的中序遍历
 *
 * 中序遍历就是根节点在中间位置的遍历
 *
 */
public class InOrderTraversal {

    public static void main(String[] args) {
        Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
        TreeNode tree = TreeNode.createTreeByArr(arr);

        InOrderTraversal solution = new InOrderTraversal();
        List<Integer> resList = solution.inorderTraversal(tree);
        System.out.println(resList);
    }


    /**
     * 中序遍历 (自己写的)
     *
     * @param root 树对象
     * @return
     */
    public List<Integer> inorderTraversal0(TreeNode root) {
        List<Integer> resList = new ArrayList<>();
        if (root == null) {
            return resList;
        }

        if (root.left != null) {
            resList.addAll(inorderTraversal(root.left));
        }
        resList.add(root.val);
        if (root.right != null) {
            resList.addAll(inorderTraversal(root.right));
        }
        return resList;
    }

    /**
     * 官方写法 递归
     *
     * @param root 树对象
     * @return
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }


    /**
     * 二叉树的中序遍历
     * 使用 栈 + 迭代遍历
     *
     * 注意
     *      1 中序遍历, 是先出栈, 再访问
     *
     * @param root 树
     * @return
     */
    public List<Integer> inorderTraversal2(TreeNode root) {
        List<Integer> resList = new ArrayList<>();

        // 使用栈结构保存节点信息 方便回溯到上层节点
        Deque<TreeNode> stack = new LinkedList<>();
        while (root != null || !stack.isEmpty()) {
            // 左节点依次入栈
            // 不断往左子树方向走, 每走一次就将当前节点保存到栈中
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            // 从栈中弹出节点, 指针指向弹出的节点, 这边就是向上层节点进行了回溯
            // 此时root==null, root.right一定为null, 执行下一次外层while代码块
            // 根节点出栈, 再去遍历root.right(以右节点为新的根节点进行遍历)
            root = stack.pop();

            // 当前节点加入结果中
            resList.add(root.val);

            // root指针移动到右子树, 继续上面整个过程
            root = root.right;
        }
        return resList;
    }


    /**
     * Morris 中序遍历
     *
     * @param root  树
     * @return
     */
    public List<Integer> inorderTraversal3(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        TreeNode pre = null;
        while (root != null) {
            // 如果左节点不为空，就将当前节点连带右子树全部挂到左节点的最右子树下面
            if (root.left != null) {
                pre = root.left;
                while (pre.right != null) {
                    pre = pre.right;
                }
                pre.right = root;

                // 将root指向root的left
                TreeNode tmp = root;
                root = root.left;

                // 当前节点和左子树断开
                tmp.left = null;
            // 左子树为空，则打印这个节点，并向右边遍历
            } else {
                res.add(root.val);
                root = root.right;
            }
        }
        return res;
    }

}
